home *** CD-ROM | disk | FTP | other *** search
/ Info-Mac 4 / Info_Mac IV CD-ROM (Pacific HiTech Inc.)(August 1994).iso / Development / Source / tarsrc Folder / create.c < prev    next >
Text File  |  1994-02-02  |  11KB  |  491 lines

  1. /*
  2.  * Macintosh Tar
  3.  *
  4.  * Modifed by Craig Ruff for use on the Macintosh.
  5.  */
  6. /*
  7.  * Create a tar archive.
  8.  *
  9.  * Written 25 Aug 1985 by John Gilmore, ihnp4!hoptoad!gnu.
  10.  *
  11.  * @(#)create.c 1.19 9/9/86 Public Domain - gnu
  12.  */
  13.  
  14. #include "tar.h"
  15.  
  16. union record        *StartHeader();
  17. extern union record    *head;
  18.  
  19. /*
  20.  * Fake stat struct for use with existing code
  21.  */
  22. extern struct stat {
  23.     long    st_size;
  24.     long    st_mtime;
  25. } hstat;
  26.  
  27. void    FinishHeader();
  28. void    ToOct();
  29. Boolean    DumpDir(), DumpFile(), FillName(), WriteEot();
  30.  
  31. /*
  32.  * Used to save pathname info while descending the directory hierarchy.
  33.  */
  34. struct PathInfo {
  35.     struct PathInfo    *next;
  36.     char        name[64];
  37. };
  38. typedef struct PathInfo PathInfo;
  39. PathInfo    pathHead;
  40.  
  41. /*
  42.  * ArCreate - manage the creation of an archive
  43.  *
  44.  *    Asks for the archive name, creates the archive and then
  45.  *    loops asking for directories to add to the archive.
  46.  */
  47. ArCreate() {
  48.     Boolean        errFound = false;
  49.     CInfoPBRec    pb;
  50.     CursHandle    cursor;
  51.  
  52.     if (OpenArchive("\pCreate Archive:", false))    /* Open for writing */
  53.         return;
  54.  
  55.     /*
  56.      * Ask for directories to add to the archive.
  57.      * Note that this is WHOLE directories.
  58.      */
  59.     while (!errFound && GetDir("\pDirectory to Archive:", false)) {
  60.         /*
  61.          * Get the catalog info for the selected directory.
  62.          */
  63.         pathHead.next = nil;
  64.         pathHead.name[0] = 0;
  65.         memset(&pb, 0, sizeof(pb));
  66.         pb.hFileInfo.ioCompletion = nil;
  67.         pb.hFileInfo.ioNamePtr = pathHead.name;
  68.         pb.hFileInfo.ioVRefNum = dirVRefNum;
  69.         pb.hFileInfo.ioDirID = dirDirID;
  70.         pb.hFileInfo.ioFDirIndex = -1;
  71.         if (PBGetCatInfo(&pb, false) != noErr) {
  72.             OSAlert("\pArCreate", "\pPBGetCatInfo", pathHead.name,
  73.                     pb.hFileInfo.ioResult);
  74.             break;
  75.  
  76.         } else {
  77.             if (((unsigned char) pathHead.name[0]) >= sizeof(pathHead.name)) {
  78.                 /*
  79.                  * Sanity check, we have overwritten our stack!
  80.                  */
  81.                 PgmAlert("\pArCreate", "\pName too long", pathHead.name);
  82.                 return(true);
  83.             }
  84.  
  85.             /*
  86.              * Add the directory to the archive,
  87.              * while printing the files being added.
  88.              */
  89.             if (WindInit())
  90.                 goto done;
  91.  
  92.             TextFace(underline);
  93.             WPrintf(header);
  94.             TextFace(0);
  95.             if ((cursor = GetCursor(watchCursor)) != nil)
  96.                 SetCursor(*cursor);
  97.  
  98.             errFound = DumpDir(&pb, &pathHead);
  99.             SetCursor(&qd.arrow);
  100.             WindEnd(pref.autoPage);
  101.             FlushEvents(everyEvent, 0);
  102.         }
  103.     }
  104.  
  105.     WriteEot();
  106. done:
  107.     CloseArchive();
  108. }
  109.  
  110. /*
  111.  * DumpDir - add a directory (possibly recursively) to the archive
  112.  */
  113. Boolean
  114. DumpDir(dir, path)
  115. CInfoPBRec    *dir;
  116. PathInfo    *path;
  117. {
  118.     union record    *header;
  119.     int        i;
  120.     Boolean        errFound = false;
  121.     CInfoPBRec    pb;
  122.     PathInfo    file;
  123.     char        *routine = "\pDumpDir";
  124.     EventRecord    e;
  125.  
  126.     /*
  127.      * Output directory header record with permissions
  128.      * FIXME, do this AFTER files, to avoid R/O dir problems?
  129.      * If Unix Std format, don't put / on end of dir name
  130.      * If old archive format, don't write record at all.
  131.      */
  132.     if (!pref.oldArch) {
  133.         /*
  134.          * If people could really read standard archives,
  135.          * this should be:        (FIXME)
  136.          * header = start_header(f_standard? p: namebuf, statbuf);
  137.          * but since they'd interpret LF_DIR records as
  138.          * regular files, we'd better put the / on the name.
  139.          */
  140.         if ((header = StartHeader(dir)) == nil)
  141.             return(true);
  142.  
  143.         if (standard)
  144.             header->header.linkflag = LF_DIR;
  145.             
  146.         FinishHeader(header);    /* Done with directory header */
  147.         head = header;
  148.         PrintHeader();
  149.     }
  150.  
  151.     file.next = nil;
  152.     path->next = &file;
  153.     
  154.     /*
  155.      * Check all entries in the directory.
  156.      * Add regular files, recurse on subdirectories.
  157.      */
  158.     for (i = 1; !errFound; i++) {
  159.         if (EventAvail(keyDownMask, &e) && (e.what != nullEvent)) {
  160.             if (
  161.                 (e.modifiers & cmdKey) &&
  162.                 ((e.message & charCodeMask) == '.')
  163.             ) {
  164.                 WaitNextEvent(keyDownMask, &e, 0L, nil);
  165.                 break;
  166.             }
  167.         }
  168.  
  169.         file.name[0] = 0;
  170.         memset(&pb, 0, sizeof(pb));
  171.         pb.hFileInfo.ioCompletion = nil;
  172.         pb.hFileInfo.ioNamePtr = file.name;
  173.         pb.hFileInfo.ioVRefNum = dir->dirInfo.ioVRefNum;
  174.         pb.hFileInfo.ioDirID = dir->dirInfo.ioDrDirID;
  175.         pb.hFileInfo.ioFDirIndex = i;
  176.         if (PBGetCatInfo(&pb, false) != noErr) {
  177.             if (pb.hFileInfo.ioResult == fnfErr)
  178.                 break;
  179.  
  180.             OSAlert(routine, "\pPBGetCatInfo", "\pDirectory search",
  181.                     pb.hFileInfo.ioResult);
  182.             return(true);
  183.         }
  184.  
  185.         if (((unsigned char) file.name[0]) >= sizeof(file.name)) {
  186.             /*
  187.              * Sanity check, we have overwritten our stack!
  188.              */
  189.             PgmAlert(routine, "\pName too long", file.name);
  190.             return(true);
  191.         }
  192.  
  193.         if (DIRECTORY(pb)) {
  194.             errFound = DumpDir(&pb, &file);
  195.  
  196.         } else {
  197.             if (!pref.tape && (pb.hFileInfo.ioFRefNum == archive)) {
  198.                 /*
  199.                  * DO NOT add the archive to itself!
  200.                  */
  201.                 ArSkipAlert();
  202.                 continue;
  203.             }
  204.  
  205.             errFound = DumpFile(&pb);
  206.         }
  207.     }
  208.  
  209.     /*
  210.      * Done with this directory, make sure we don't run out
  211.      * of working directories.
  212.      */
  213.     path->next = nil;
  214.     return(errFound);
  215. }
  216.  
  217. /*
  218.  * DumpFile - Dump a single file.
  219.  *
  220.  *    Result is 1 for success, 0 for failure.
  221.  */
  222. Boolean
  223. DumpFile(file)
  224. CInfoPBRec    *file;
  225. {
  226.     union record    *header;
  227.     register char    *p;
  228.     char        *buf;
  229.     HParamBlockRec    fpb;
  230.     long        bufsize, count, i;
  231.     Boolean        ret;
  232.     register long    sizeleft;
  233.     register union record     *start;
  234.     char        *routine = "\pDumpFile";
  235.  
  236.     if ((header = StartHeader(file)) == nil)
  237.         return(true);
  238.  
  239.     FinishHeader(header);
  240.     /*
  241.      * Get the size of the file.
  242.      * Don't bother opening it if it is zero length.
  243.      */
  244.     head = header;
  245.     hstat.st_size = file->hFileInfo.ioFlLgLen;
  246.     PrintHeader();
  247.     if ((sizeleft = file->hFileInfo.ioFlLgLen) == 0)
  248.         return(false);
  249.  
  250.     memset(&fpb, 0, sizeof(fpb));
  251.     fpb.fileParam.ioCompletion = nil;
  252.     fpb.fileParam.ioNamePtr = file->hFileInfo.ioNamePtr;
  253.     fpb.fileParam.ioVRefNum = file->hFileInfo.ioVRefNum;
  254.     fpb.fileParam.ioFVersNum = 0;
  255.     fpb.fileParam.ioDirID = file->hFileInfo.ioFlParID;
  256.     fpb.ioParam.ioPermssn = fsRdPerm;
  257.     fpb.ioParam.ioMisc = nil;
  258.     if (PBHOpen(&fpb, false) != noErr) {
  259.         OSAlert(routine, "\pPBHOpen", file->hFileInfo.ioNamePtr,
  260.                 fpb.fileParam.ioResult);
  261.         return(true);
  262.     }
  263.  
  264.     /*
  265.      * Dump the file to the archive.
  266.      * Note: this only dumps the data fork!
  267.      */
  268.     ret = false;
  269.     while (sizeleft > 0) {
  270.         if ((start = FindRec()) == nil) {
  271.             ret = true;
  272.             break;
  273.         }
  274.             
  275.         bufsize = EndOfRecs()->charptr - start->charptr;
  276.         buf = start->charptr;
  277.     again:
  278.         count = (sizeleft < bufsize) ? sizeleft : bufsize;
  279.         fpb.ioParam.ioBuffer = buf;
  280.         fpb.ioParam.ioReqCount = count;
  281.         fpb.ioParam.ioPosMode = fsAtMark;
  282.         fpb.ioParam.ioPosOffset = 0;
  283.         if (PBRead((ParmBlkPtr) &fpb, false) != noErr) {
  284.             OSAlert(routine, "\pPBRead", file->hFileInfo.ioNamePtr,
  285.                     fpb.ioParam.ioResult);
  286.             ret = true;
  287.             break;
  288.         }
  289.  
  290.         count = fpb.ioParam.ioActCount;
  291.         if (pref.cvtNl) {
  292.             /*
  293.              * Convert returns to newlines for Unix compat.
  294.              */
  295.             for (i = count, p = buf; --i >= 0; p++)
  296.                 if (*p == RETURN)
  297.                     *p = LF;
  298.         }
  299.  
  300.         sizeleft -= count;
  301.         UseRec(start + (count - 1) / RECORDSIZE);
  302.     }
  303.     
  304.     PBClose((ParmBlkPtr) &fpb, false);
  305.     /* Clear last block garbage to zeros, FIXME */
  306.     return(ret);
  307. }
  308.  
  309.  
  310. /*
  311.  * Make a header block for the file  name  whose stat info is  st .
  312.  * Return header pointer for success, NULL if the name is too long.
  313.  */
  314. union record *
  315. StartHeader(pb)
  316. CInfoPBRec    *pb;
  317. {
  318.     register union record *header;
  319.     Boolean    directory = DIRECTORY(*pb);
  320.  
  321.     if ((header = (union record *) FindRec()) == nil)
  322.         return(nil);
  323.         
  324.     memset(header->charptr, 0, sizeof(union record)); /* XXX speed up */
  325.     /*
  326.      * Generate the pathname, make sure we don't overflow
  327.      * the field in the tar header.
  328.      */
  329.     if (FillName(header, directory)) {
  330.         char    buf[NAMSIZ + 1];
  331.  
  332.         buf[0] = NAMSIZ;
  333.         memcpy(&buf[1], header->header.name, NAMSIZ);
  334.         PgmAlert("\pStartHeader", "\pName too long", buf);
  335.         return(nil);
  336.     }
  337.  
  338.     /*
  339.      * Fake the file mode, uid, gid.
  340.      * Convert from Mac based time to Unix based time.
  341.      */
  342.     ToOct((directory) ? 0755L : 0644L, 8,  header->header.mode);
  343.     ToOct(0L, 8,  header->header.uid);
  344.     ToOct(0L, 8,  header->header.gid);
  345.     ToOct((directory) ? 0L : pb->hFileInfo.ioFlLgLen, 1+12,
  346.             header->header.size);
  347.     ToOct((directory) ? pb->dirInfo.ioDrMdDat - TIMEDIFF :
  348.                 pb->hFileInfo.ioFlMdDat - TIMEDIFF,
  349.             1+12, header->header.mtime);
  350.     /* header->header.linkflag is left as null */
  351.     return(header);
  352. }
  353.  
  354. /* 
  355.  * Finish off a filled-in header block and write it out.
  356.  */
  357. void
  358. FinishHeader(header)
  359. register union record *header;
  360. {
  361.     register int    i;
  362.     register long    sum;
  363.     register char    *p;
  364.  
  365.     memcpy(header->header.chksum, CHKBLANKS, sizeof(header->header.chksum));
  366.     sum = 0;
  367.     p = header->charptr;
  368.     for (i = sizeof(union record); --i >= 0; ) {
  369.         /*
  370.          * We can't use unsigned char here because of old compilers,
  371.          * e.g. V7.
  372.          */
  373.         sum += 0xFF & *p++;
  374.     }
  375.  
  376.     /*
  377.      * Fill in the checksum field.  It's formatted differently
  378.      * from the other fields:  it has [6] digits, a null, then a
  379.      * space -- rather than digits, a space, then a null.
  380.      * We use to_oct then write the null in over to_oct's space.
  381.      * The final space is already there, from checksumming, and
  382.      * to_oct doesn't modify it.
  383.      *
  384.      * This is a fast way to do:
  385.      * (void) sprintf(header->header.chksum, "%6o", sum);
  386.      */
  387.     ToOct((long) sum, 8, header->header.chksum);
  388.     header->header.chksum[6] = '\0';    /* Zap the space */
  389.     UseRec(header);
  390.     return;
  391. }
  392.  
  393.  
  394. /*
  395.  * Quick and dirty octal conversion.
  396.  * Converts long "value" into a "digs"-digit field at "where",
  397.  * including a trailing space and room for a null.  "digs"==3 means
  398.  * 1 digit, a space, and room for a null.
  399.  *
  400.  * We assume the trailing null is already there and don't fill it in.
  401.  * This fact is used by start_header and finish_header, so don't change it!
  402.  *
  403.  * This should be equivalent to:
  404.  *    (void) sprintf(where, "%*lo ", digs-2, value);
  405.  * except that sprintf fills in the trailing null and we don't.
  406.  */
  407. void
  408. ToOct(value, digs, where)
  409. register long    value;
  410. register int    digs;
  411. register char    *where;
  412. {
  413.     --digs;                /* Trailing null slot is left alone */
  414.     where[--digs] = ' ';        /* Put in the space, though */
  415.  
  416.     /* Produce the digits -- at least one */
  417.     do {
  418.         where[--digs] = '0' + (value & 7);    /* one octal digit */
  419.         value >>= 3;
  420.     } while (digs > 0 && value != 0);
  421.  
  422.     /* Leading spaces, if necessary */
  423.     while (digs > 0)
  424.         where[--digs] = ' ';
  425.  
  426. }
  427.  
  428. /*
  429.  * Write the EOT block(s).
  430.  */
  431. Boolean
  432. WriteEot()
  433. {
  434.     union record *p;
  435.  
  436.     if ((p = FindRec()) == nil)
  437.         return(true);
  438.         
  439.     memset(p->charptr, 0, RECORDSIZE);
  440.     UseRec(p);
  441.     /* FIXME, only one EOT block should be needed. */
  442.     if ((p = FindRec()) == nil)
  443.         return(true);
  444.         
  445.     memset(p->charptr, 0, RECORDSIZE);
  446.     UseRec(p);
  447.     return(false);
  448. }
  449.  
  450. /*
  451.  * FillName - generate the file or directory pathname
  452.  *
  453.  *    Converts to Unix style pathnames.
  454.  *    Appends a '/' for a directory.
  455.  */
  456. Boolean
  457. FillName(header, directory)
  458. register union record *header;
  459. Boolean    directory;
  460. {
  461.     register PathInfo    *p;
  462.     register char        *d, *s;
  463.     char            c;
  464.     int            i;
  465.  
  466.     d = header->header.name;
  467.     for (p = &pathHead; p != nil; p = p->next) {
  468.         s = &p->name[1];
  469.         for (i = p->name[0]; i > 0; i--) {
  470.             c = *s++;
  471.             if (c == '/')
  472.                 *d++ = ':';
  473.  
  474.             else if ((c <= ' ') || (c >= 0x7f))
  475.                 *d++ = '_';
  476.  
  477.             else
  478.                 *d++ = c;
  479.         }
  480.  
  481.         *d++ = (p->next == nil) ? '\0' : '/';
  482.     }
  483.  
  484.     if (directory) {
  485.         *(d - 1) = '/';
  486.         *d = '\0';
  487.     }
  488.  
  489.     return((d - header->header.name) > NAMSIZ);
  490. }
  491.